package com.cly.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cly.common.content.AuthCodeContent;
import com.cly.common.content.UserContent;
import com.cly.common.entity.result.Result;
import com.cly.common.entity.user.dto.UserLoginDTO;
import com.cly.common.entity.user.dto.UserRegisterDTO;
import com.cly.common.entity.user.po.User;
import com.cly.common.entity.user.vo.UserLoginVO;
import com.cly.common.exception.AuthCodeException;
import com.cly.common.exception.CommonException;
import com.cly.common.properties.JwtProperties;
import com.cly.common.util.JwtUtil;
import com.cly.common.util.ThreadLocalUtil;
import com.cly.user.mapper.UserMapper;
import com.cly.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 验证码登入
     * @param userLoginDTO
     */
    @Override
    public Result<UserLoginVO> loginAuthCode(UserLoginDTO userLoginDTO) {
        // 判断验证码是否为空
        if (StrUtil.isBlank(userLoginDTO.getAuthCode())) {
            throw new AuthCodeException(AuthCodeContent.AUTH_CODE_NULL);
        }
        // 从redis中获取到验证码
        String authCode = stringRedisTemplate.opsForValue().
                get(AuthCodeContent.AUTH_CODE_LOGIN_REDIS_KEY + userLoginDTO.getPhoneNumber());
        // 查询数据库中的用户信息
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User user = this.getOne(queryWrapper.eq(User::getUserPhone, userLoginDTO.getPhoneNumber()));
        // 判断用户是否存在
        if (user == null) {
            // 不存在，抛出错误
            throw new AuthCodeException(UserContent.USER_NULL);
        }
        // 校验缓存验证码和登入验证码是否一致
        if(userLoginDTO.getAuthCode().equals(authCode)){
            // 一致，且数据库中存在该用户信息，生产JWT与用户数据一起返回前端
            String jwt = JwtUtil.getJWT(jwtProperties.getSecretKey(), jwtProperties.getTime(), user.getId());
            UserLoginVO userLoginVO = BeanUtil.copyProperties(user, UserLoginVO.class);
            userLoginVO.setJWT(jwt);
            return Result.success(userLoginVO);
        }else{
            // 不一致，抛出异常
            throw new AuthCodeException(AuthCodeContent.AUTH_CODE_ERROR);
        }
    }

    /**
     * 密码登入
     * @param userLoginDTO
     * @return
     */
    @Override
    public Result<UserLoginVO> loginPassword(UserLoginDTO userLoginDTO) {
        //  判断密码是否为空
        if (StrUtil.isBlank(userLoginDTO.getPassword())) {
            throw new CommonException(UserContent.USER_PASSWORD_NULL);
        }
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User user = this.getOne(queryWrapper.eq(User::getUserPhone, userLoginDTO.getPhoneNumber()));
        // 判断用户是否存在
        if (user == null) {
            // 不存在，抛出错误
            throw new AuthCodeException(UserContent.USER_NULL);
        }
        String userLoginDTOPassword = userLoginDTO.getPassword();
        userLoginDTOPassword = DigestUtils.md5DigestAsHex(userLoginDTOPassword.getBytes());
        if(user.getPassword().equals(userLoginDTOPassword)){
            // 一致，生产JWT与用户数据一起返回前端
            String jwt = JwtUtil.getJWT(jwtProperties.getSecretKey(), jwtProperties.getTime(), user.getId());
            UserLoginVO userLoginVO = BeanUtil.copyProperties(user, UserLoginVO.class);
            userLoginVO.setJWT(jwt);
            return Result.success(userLoginVO);
        }else{
            // 不一致，抛出异常
            throw new CommonException(UserContent.USER_LOGIN_ERROR);
        }
    }

    /**
     * 注册
     * @param userRegisterDTO
     * @return
     */
    @Override
    public Result registerByPhone(UserRegisterDTO userRegisterDTO) {
        // 查询当前手机号是否已经注册
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        User userDB = this.getOne(queryWrapper.eq(User::getUserPhone, userRegisterDTO.getPhoneNumber()));
        if(userDB != null){
            // 已经注册过
            throw new CommonException(UserContent.USER_REGISTER_PHONE_EXIST);
        }
        String authCode = stringRedisTemplate.opsForValue()
                .get(AuthCodeContent.AUTH_CODE_REGISTER_REDIS_KEY + userRegisterDTO.getPhoneNumber());
        if ( !authCode.equals(userRegisterDTO.getAuthCode())) {
            throw new CommonException(AuthCodeContent.AUTH_CODE_ERROR);
        }
        User user = BeanUtil.copyProperties(userRegisterDTO, User.class);
        try {
            this.save(user);
        } catch (Exception e) {
            throw new CommonException(UserContent.USER_REGISTER_ERROR, e);
        }
        return Result.success();
    }

    /**
     * 扣减余额
     * @param password
     * @param amount
     */
    @Override
    public void deductMoney(String password, Integer amount) {
        if(StrUtil.isBlank(password)){
            throw new CommonException(UserContent.USER_PASSWORD_NULL);
        }
        log.info("开始扣款");
        // 先获取用户正确的密码进行密码校验
        User user = this.getById(ThreadLocalUtil.get());
        if (user == null || !(user.getPassword().equals(DigestUtils.md5DigestAsHex(password.getBytes())))) {
            throw new CommonException(UserContent.USER_LOGIN_ERROR);
        }
        try {
            LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.setSql("balance = balance - " + amount).eq(User::getId, user.getId());
            this.update(updateWrapper);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
